// SPDX-License-Identifier: Mulan PSL v2
/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * This software is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#[cfg(test)]
mod tests {
    use crate::ipc::bytewise::BytewiseWriter;
    use crate::ipc::message::{Argument, ArgumentFlag, Request};
    use crate::recorder::{RecorderConfig, JOURNAL_MAGIC};
    use crate::types::xgpu_recorder::v1::*;
    use std::ptr;
    use std::time::{SystemTime, UNIX_EPOCH};

    #[test]
    fn test_protobuf_types() {
        // test RecorderHeader
        let header = RecorderHeader::new(); // Use new() instead of default()
        assert_eq!(header.magic, JOURNAL_MAGIC);
        assert!(header.version.is_some());
        assert!(header.context.is_some());

        // Test RecordBlock
        let block = RecordBlock::default();
        assert_eq!(block.block_id, 0);
        assert_eq!(block.timestamp_ns, 0);
        assert_eq!(block.thread_id, 0);
        assert!(block.payload.is_none());

        println!("✅ Protobuf types basic functionality test passed");
    }

    /// Test Request serialization and deserialization
    #[test]
    fn test_request_serialization() {
        // Create test Request
        let test_value = 42u32;
        let test_arg = Argument::from_ref(&test_value, ArgumentFlag::ARG_IN);
        let original_request = Request::with_arg(0x12345678, test_arg);

        // Test serialization (if serialization function is available)
        // Test Request's own serialization capability here
        use crate::ipc::bytewise::{BytewiseBuffer, BytewiseReadOwned, BytewiseWrite};

        let mut buffer = vec![0u8; 1024];
        let mut writer = BytewiseBuffer::new(&mut buffer);

        // Serialize
        original_request
            .write_to(&mut writer)
            .expect("Serialization failed");
        let written_size = writer.written_bytes();

        // Deserialize
        let mut reader = BytewiseBuffer::new(&buffer[..written_size]);
        let reconstructed_request =
            Request::read_from(&mut reader).expect("Deserialization failed");

        // Verify data
        assert_eq!(
            reconstructed_request.method_id(),
            original_request.method_id()
        );
        assert_eq!(reconstructed_request.argc(), original_request.argc());

        println!("✅ Request serialization test passed");
    }

    /// Test RecordBlock creation and validation
    #[test]
    fn test_record_block_creation() {
        // Create test payload
        let test_data = b"test_request_data";
        let payload = RecordPayload {
            length: test_data.len() as u64,
            crc32_checksum: crc32fast::hash(test_data),
            request_data: test_data.to_vec(),
        };

        // Create record block
        let block = RecordBlock {
            block_id: 1,
            timestamp_ns: 123456789,
            thread_id: 999,
            payload: Some(payload),
        };

        // Verify data
        assert_eq!(block.block_id, 1);
        assert_eq!(block.timestamp_ns, 123456789);
        assert_eq!(block.thread_id, 999);
        assert!(block.payload.is_some());

        let payload = block.payload.as_ref().unwrap();
        assert_eq!(payload.length, test_data.len() as u64);
        assert_eq!(
            <Vec<u8> as AsRef<[u8]>>::as_ref(&payload.request_data),
            test_data
        );

        println!("✅ RecordBlock creation test passed");
    }

    /// Test CRC32 checksum calculation
    #[test]
    fn test_crc32_checksum() {
        let test_data = b"Hello, World!";
        let checksum1 = crc32fast::hash(test_data);
        let checksum2 = crc32fast::hash(test_data);

        // Same data should have the same checksum
        assert_eq!(checksum1, checksum2);

        // Different data should have different checksums
        let other_data = b"Hello, Rust!";
        let checksum3 = crc32fast::hash(other_data);
        assert_ne!(checksum1, checksum3);

        println!("✅ CRC32 checksum test passed");
    }

    /// Test simplified configuration structure
    #[test]
    fn test_recorder_config() {
        let default_config = RecorderConfig::default();
        assert_eq!(default_config.output_path.to_str().unwrap(), "requests.rec");
        assert_eq!(default_config.buffer_size, 64 * 1024);
        assert_eq!(default_config.max_file_size, 1024 * 1024 * 1024);
        assert!(default_config.checksum_enabled);

        // Test custom configuration
        let mut custom_config = default_config.clone();
        custom_config.buffer_size = 128 * 1024;
        custom_config.compression = true;

        assert_eq!(custom_config.buffer_size, 128 * 1024);
        assert!(custom_config.compression);

        println!("✅ RecorderConfig test passed");
    }

    /// Test timestamp generation
    #[test]
    fn test_timestamp_generation() {
        use std::time::{SystemTime, UNIX_EPOCH};

        let timestamp1 = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_nanos() as u64;

        std::thread::sleep(std::time::Duration::from_millis(1));

        let timestamp2 = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_nanos() as u64;

        // Timestamps should be increasing
        assert!(timestamp2 > timestamp1);

        println!("✅ Timestamp generation test passed");
    }

    /// Integrated test: Verify basic feasibility of the entire design
    #[test]
    fn test_integrated_design_viability() {
        // 1. Create test Request
        let test_values = [1u32, 2u32, 3u32];
        let test_args = test_values
            .iter()
            .map(|v| Argument::from_ref(v, ArgumentFlag::ARG_IN))
            .collect::<Vec<_>>();
        let original_request = Request::with_args(0xDEADBEEF, test_args);

        // 2. Test Request serialization
        use crate::ipc::bytewise::{BytewiseBuffer, BytewiseReadOwned, BytewiseWrite};
        let mut buffer = vec![0u8; 1024];
        let mut writer = BytewiseBuffer::new(&mut buffer);
        original_request
            .write_to(&mut writer)
            .expect("Serialization failed");
        let written_size = writer.written_bytes();

        // 3. Create RecordPayload
        let serialized_data = &buffer[..written_size];
        let payload = RecordPayload {
            length: serialized_data.len() as u64,
            crc32_checksum: crc32fast::hash(serialized_data),
            request_data: serialized_data.to_vec(),
        };

        // 4. Create RecordBlock
        let block = RecordBlock {
            block_id: 1,
            timestamp_ns: SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap()
                .as_nanos() as u64,
            thread_id: 12345,
            payload: Some(payload),
        };

        // 5. Verify data integrity
        let payload = block.payload.as_ref().unwrap();

        // Verify checksum
        let computed_checksum = crc32fast::hash(&payload.request_data);
        assert_eq!(computed_checksum, payload.crc32_checksum);

        // Verify Request can be reconstructed
        let mut reader = BytewiseBuffer::new(&payload.request_data);
        let reconstructed_request =
            Request::read_from(&mut reader).expect("Deserialization failed");

        assert_eq!(
            reconstructed_request.method_id(),
            original_request.method_id()
        );
        assert_eq!(reconstructed_request.argc(), original_request.argc());

        println!("✅ Integrated design viability test passed");
        println!("🎉 New recorder design is working correctly!");
    }

    /// Test RequestPlayer::from_file functionality
    #[test]
    fn test_player_from_file() {
        use crate::recorder::logger::RequestLogger;
        use crate::recorder::player::RequestPlayer;
        use crate::recorder::RecorderConfig;
        use tempfile::NamedTempFile;

        // 1. Create temporary file for testing
        let temp_file = NamedTempFile::new().expect("Failed to create temporary file");
        let file_path = temp_file.path();

        // 2. Create test request data
        let test_values = [1u32, 2u32, 3u32];
        let test_args = test_values
            .iter()
            .map(|v| Argument::from_ref(v, ArgumentFlag::ARG_IN))
            .collect::<Vec<_>>();
        let test_request = Request::with_args(0xDEADBEEFu64, test_args);

        // 3. Use RequestLogger to write test data
        {
            let config = RecorderConfig {
                output_path: file_path.to_path_buf(),
                buffer_size: 1024,
                max_file_size: 1024 * 1024,
                sync_interval_secs: 5,
                checksum_enabled: true,
                compression: false,
            };

            let mut logger = RequestLogger::new(config).expect("Failed to create logger");
            logger
                .log_request(&test_request)
                .expect("Failed to record request");
            logger.close().expect("Failed to close logger");
        }

        // 4. Test creating RequestPlayer from file
        let mut player = RequestPlayer::from_file(file_path).expect("Failed to create player");

        // 5. Verify file header
        let header = player.header();
        assert_eq!(header.magic, JOURNAL_MAGIC);
        assert!(header.version.is_some());
        assert!(header.context.is_some());

        // 6. Verify initial state
        assert_eq!(player.position(), 0);

        // 7. Read and verify record block
        let mut block_with_bytewise = player
            .next_bytewise::<Request>()
            .expect("Failed to read request")
            .expect("Should have at least one request");

        // 8. Verify read request data
        let reconstructed_request = block_with_bytewise
            .bytewise()
            .expect("Failed to rebuild request");
        assert_eq!(reconstructed_request.method_id(), test_request.method_id());
        assert_eq!(reconstructed_request.argc(), test_request.argc());

        // 9. Verify position update
        assert_eq!(player.position(), 1);

        // 10. Verify end of file
        let next_request = player
            .next_bytewise::<Request>()
            .expect("Reading next request should succeed");
        assert!(next_request.is_none(), "Should be no more requests");

        println!("✅ RequestPlayer::from_file test passed");
    }

    /// Test RequestPlayer::from_file error handling
    #[test]
    fn test_player_from_file_error_handling() {
        use crate::recorder::player::RequestPlayer;

        // 1. Test non-existent file
        let result = RequestPlayer::from_file("/nonexistent/file.rec");
        assert!(result.is_err(), "Non-existent file should return error");

        // 2. Test invalid file format
        use tempfile::NamedTempFile;
        let temp_file = NamedTempFile::new().expect("Failed to create temporary file");
        std::fs::write(temp_file.path(), b"invalid data").expect("Failed to write invalid data");

        let result = RequestPlayer::from_file(temp_file.path());
        assert!(result.is_err(), "Invalid file format should return error");

        println!("✅ RequestPlayer::from_file error handling test passed");
    }

    /// Test RequestPlayer reset functionality
    #[test]
    fn test_player_reset() {
        use crate::recorder::logger::RequestLogger;
        use crate::recorder::player::RequestPlayer;
        use crate::recorder::RecorderConfig;
        use tempfile::NamedTempFile;

        // 1. Create temporary file and write multiple requests
        let temp_file = NamedTempFile::new().expect("Failed to create temporary file");
        let file_path = temp_file.path();

        let test_requests = [
            Request::with_arg(0x1111u64, Argument::from_ref(&100u32, ArgumentFlag::ARG_IN)),
            Request::with_arg(0x2222u64, Argument::from_ref(&200u32, ArgumentFlag::ARG_IN)),
            Request::with_arg(0x3333u64, Argument::from_ref(&300u32, ArgumentFlag::ARG_IN)),
        ];

        {
            let config = RecorderConfig {
                output_path: file_path.to_path_buf(),
                buffer_size: 1024,
                max_file_size: 1024 * 1024,
                sync_interval_secs: 5,
                checksum_enabled: true,
                compression: false,
            };

            let mut logger = RequestLogger::new(config).expect("Failed to create logger");
            for request in &test_requests {
                logger
                    .log_request(request)
                    .expect("Failed to record request");
            }
            logger.close().expect("Failed to close logger");
        }

        // 2. Create player and read partial data
        let mut player = RequestPlayer::from_file(file_path).expect("Failed to create player");

        // Read first request
        let mut first_block_with_bytewise = player
            .next_bytewise::<Request>()
            .expect("Failed to read first request")
            .expect("Should have first request");
        assert_eq!(player.position(), 1);

        // Read second request
        let _second_block_with_bytewise = player
            .next_bytewise::<Request>()
            .expect("Failed to read second request")
            .expect("Should have second request");
        assert_eq!(player.position(), 2);

        // 3. Reset player
        player.reset().expect("Failed to reset player");
        assert_eq!(player.position(), 0);

        // 4. Verify reset allows re-reading
        let mut reset_first_block_with_bytewise = player
            .next_bytewise::<Request>()
            .expect("Failed to read first request after reset")
            .expect("Should have first request after reset");
        assert_eq!(player.position(), 1);

        // Verify data consistency
        let original_request = first_block_with_bytewise
            .bytewise()
            .expect("Failed to get original request");
        let reset_request = reset_first_block_with_bytewise
            .bytewise()
            .expect("Failed to get reset request");
        assert_eq!(original_request.method_id(), reset_request.method_id());

        let original_args = original_request.args();
        let reset_args = reset_request.args();
        assert_eq!(
            original_args[0].downcast_ref::<u32>().unwrap(),
            reset_args[0].downcast_ref::<u32>().unwrap(),
        );

        println!("✅ RequestPlayer reset test passed");
    }

    /// Test RequestPlayer iterator functionality
    #[test]
    fn test_player_iterator() {
        use crate::recorder::logger::RequestLogger;
        use crate::recorder::player::RequestPlayer;
        use crate::recorder::RecorderConfig;
        use tempfile::NamedTempFile;

        // 1. Create temporary file and write test data
        let temp_file = NamedTempFile::new().expect("Failed to create temporary file");
        let file_path = temp_file.path();

        let test_requests = vec![
            Request::with_arg(0x1111u64, Argument::from_ref(&100u32, ArgumentFlag::ARG_IN)),
            Request::with_arg(0x2222u64, Argument::from_ref(&200u32, ArgumentFlag::ARG_IN)),
            Request::with_arg(0x3333u64, Argument::from_ref(&300u32, ArgumentFlag::ARG_IN)),
        ];

        {
            let config = RecorderConfig {
                output_path: file_path.to_path_buf(),
                buffer_size: 1024,
                max_file_size: 1024 * 1024,
                sync_interval_secs: 5,
                checksum_enabled: true,
                compression: false,
            };

            let mut logger = RequestLogger::new(config).expect("Failed to create logger");
            for request in &test_requests {
                logger
                    .log_request(request)
                    .expect("Failed to record request");
            }
            logger.close().expect("Failed to close logger");
        }

        // Count records by iterating through them
        let mut count = 0;
        let mut player_clone =
            RequestPlayer::from_file(file_path).expect("Failed to create player");
        while let Some(Ok(_)) = player_clone.next_block().transpose() {
            count += 1;
        }
        assert_eq!(count, 3, "Should have 3 records");

        // Iterate through records
        let player = RequestPlayer::from_file(file_path).expect("Failed to create player");
        let mut count = 0;
        for result in player {
            result.expect("Failed to read record");
            count += 1;
        }
        assert_eq!(count, 3, "Should have 3 records");

        println!("✅ RequestPlayer iterator test passed");
    }

    #[test]
    fn test_player_all_arguments() {
        use crate::recorder::logger::RequestLogger;
        use crate::recorder::player::RequestPlayer;
        use crate::recorder::RecorderConfig;
        use tempfile::NamedTempFile;

        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
        struct Point {
            x: i32,
            y: i32,
        }

        // 1. Create temporary file and write test data
        let temp_file = NamedTempFile::new().expect("Failed to create temporary file");
        let file_path = temp_file.path();

        let value = Point { x: 10, y: 20 };

        let mut mut_value = Point { x: 30, y: 40 };
        let slice_value = [1u32, 2, 3, 4];
        let mut mut_slice_value = [5u32, 6, 7, 8];

        let test_requests = vec![
            Request::with_arg(
                0x1111u64,
                Argument::from_value(100u32, ArgumentFlag::ARG_IN),
            ),
            Request::with_arg(0x2222u64, unsafe {
                Argument::from_ptr(ptr::from_ref(&value), ArgumentFlag::ARG_IN)
            }),
            Request::with_arg(0x3333u64, unsafe {
                Argument::from_mut_ptr(ptr::from_mut(&mut mut_value), ArgumentFlag::ARG_IN)
            }),
            Request::with_arg(0x4444u64, Argument::from_ref(&value, ArgumentFlag::ARG_IN)),
            Request::with_arg(
                0x5555u64,
                Argument::from_mut(&mut mut_value, ArgumentFlag::ARG_IN),
            ),
            Request::with_arg(
                0x6666u64,
                Argument::from_slice(&slice_value, ArgumentFlag::ARG_IN),
            ),
            Request::with_arg(
                0x7777u64,
                Argument::from_mut_slice(&mut mut_slice_value, ArgumentFlag::ARG_IN),
            ),
        ];

        {
            let config = RecorderConfig {
                output_path: file_path.to_path_buf(),
                buffer_size: 1024,
                max_file_size: 1024 * 1024,
                sync_interval_secs: 5,
                checksum_enabled: true,
                compression: false,
            };

            let mut logger = RequestLogger::new(config).expect("Failed to create logger");
            for request in &test_requests {
                logger
                    .log_request(request)
                    .expect("Failed to record request");
            }
            logger.close().expect("Failed to close logger");
        }

        // 2. Create player and iterate through records
        let mut player = RequestPlayer::from_file(file_path).expect("Failed to create player");

        // Count records by iterating through them
        let mut count = 0;
        let mut player_clone =
            RequestPlayer::from_file(file_path).expect("Failed to create player");
        while let Some(Ok(_)) = player_clone.next_block().transpose() {
            count += 1;
        }
        assert_eq!(count, 7, "Should have 7 records");

        // Verify each request type and its arguments
        // Request 1: from_value
        let mut block_with_bytewise = player
            .next_bytewise::<Request>()
            .expect("Failed to read request")
            .expect("Should have first request");
        let request = block_with_bytewise
            .bytewise()
            .expect("Failed to rebuild request");
        assert_eq!(request.method_id(), 0x1111u64);
        assert_eq!(request.argc(), 1);
        let args = request.args();
        assert_eq!(args[0].downcast::<u32>().expect("Should be u32"), 100u32);

        // Request 2: from_ptr
        let mut block_with_bytewise = player
            .next_bytewise::<Request>()
            .expect("Failed to read request")
            .expect("Should have second request");
        let request = block_with_bytewise
            .bytewise()
            .expect("Failed to rebuild request");
        assert_eq!(request.method_id(), 0x2222u64);
        assert_eq!(request.argc(), 1);
        let args = request.args();
        assert_eq!(args[0].downcast::<Point>().expect("Should be Point"), value);

        // Request 3: from_mut_ptr
        let mut block_with_bytewise = player
            .next_bytewise::<Request>()
            .expect("Failed to read request")
            .expect("Should have third request");
        let request = block_with_bytewise
            .bytewise()
            .expect("Failed to rebuild request");
        assert_eq!(request.method_id(), 0x3333u64);
        assert_eq!(request.argc(), 1);
        let args = request.args();
        assert_eq!(
            args[0].downcast::<Point>().expect("Should be Point"),
            mut_value
        );

        // Request 4: from_ref
        let mut block_with_bytewise = player
            .next_bytewise::<Request>()
            .expect("Failed to read request")
            .expect("Should have fourth request");
        let request = block_with_bytewise
            .bytewise()
            .expect("Failed to rebuild request");
        assert_eq!(request.method_id(), 0x4444u64);
        assert_eq!(request.argc(), 1);
        let args = request.args();
        assert_eq!(args[0].downcast::<Point>().expect("Should be Point"), value);

        // Request 5: from_mut
        let mut block_with_bytewise = player
            .next_bytewise::<Request>()
            .expect("Failed to read request")
            .expect("Should have fifth request");
        let request = block_with_bytewise
            .bytewise()
            .expect("Failed to rebuild request");
        assert_eq!(request.method_id(), 0x5555u64);
        assert_eq!(request.argc(), 1);
        let args = request.args();
        assert_eq!(
            args[0].downcast::<Point>().expect("Should be Point"),
            mut_value
        );

        // Request 6: from_slice
        let mut block_with_bytewise = player
            .next_bytewise::<Request>()
            .expect("Failed to read request")
            .expect("Should have sixth request");
        let request = block_with_bytewise
            .bytewise()
            .expect("Failed to rebuild request");
        assert_eq!(request.method_id(), 0x6666u64);
        assert_eq!(request.argc(), 1);
        let args = request.args();
        assert_eq!(
            args[0]
                .downcast_slice::<u32>()
                .expect("Should be slice of u32"),
            &slice_value
        );

        // Request 7: from_mut_slice
        let mut block_with_bytewise = player
            .next_bytewise::<Request>()
            .expect("Failed to read request")
            .expect("Should have seventh request");
        let request = block_with_bytewise
            .bytewise()
            .expect("Failed to rebuild request");
        assert_eq!(request.method_id(), 0x7777u64);
        assert_eq!(request.argc(), 1);
        let args = request.args();
        assert_eq!(
            args[0]
                .downcast_slice::<u32>()
                .expect("Should be slice of u32"),
            &mut_slice_value
        );

        // Verify end of file
        let next_request = player
            .next_bytewise::<Request>()
            .expect("Reading next request should succeed");
        assert!(next_request.is_none(), "Should be no more requests");

        // 9. Reset player
        player.reset().expect("Failed to reset player");
        assert_eq!(player.position(), 0);

        // Create a new player for the iterator test to avoid borrowing issues
        for result in player {
            let mut record = result.expect("Failed to read record");
            let rebuilt_request = RequestPlayer::rebuild_bytewise::<Request>(&mut record)
                .expect("Failed to rebuild request");
            match rebuilt_request.method_id() {
                0x1111u64 => {
                    assert_eq!(rebuilt_request.argc(), 1);
                    let args = rebuilt_request.args();
                    assert_eq!(args[0].downcast::<u32>().expect("Should be u32"), 100u32);
                }
                0x2222u64 => {
                    assert_eq!(rebuilt_request.argc(), 1);
                    let args = rebuilt_request.args();
                    assert_eq!(args[0].downcast::<Point>().expect("Should be Point"), value);
                }
                0x3333u64 => {
                    assert_eq!(rebuilt_request.argc(), 1);
                    let args = rebuilt_request.args();
                    assert_eq!(
                        args[0].downcast::<Point>().expect("Should be Point"),
                        mut_value
                    );
                }
                0x4444u64 => {
                    assert_eq!(rebuilt_request.argc(), 1);
                    let args = rebuilt_request.args();
                    assert_eq!(args[0].downcast::<Point>().expect("Should be Point"), value);
                }
                0x5555u64 => {
                    assert_eq!(rebuilt_request.argc(), 1);
                    let args = rebuilt_request.args();
                    assert_eq!(
                        args[0].downcast::<Point>().expect("Should be Point"),
                        mut_value
                    );
                }
                0x6666u64 => {
                    assert_eq!(rebuilt_request.argc(), 1);
                    let args = rebuilt_request.args();
                    assert_eq!(
                        args[0]
                            .downcast_slice::<u32>()
                            .expect("Should be slice of u32"),
                        &slice_value
                    );
                }
                0x7777u64 => {
                    assert_eq!(rebuilt_request.argc(), 1);
                    let args = rebuilt_request.args();
                    assert_eq!(
                        args[0]
                            .downcast_slice::<u32>()
                            .expect("Should be slice of u32"),
                        &mut_slice_value
                    );
                }
                _ => panic!("Unknown method ID"),
            }
        }

        println!("✅ RequestPlayer all arguments test passed");
    }
}
